const AppError = require('../utils/appError')

// 开发环境错误提示
const sendErrorDev = (err, res) => {
  res.status(err.statusCode).json({
    status: err.status,
    err: err,
    message: err.message,
    stack: err.stack,
  })
}

// 生产环境错误提示
const sendErrorProd = (err, res) => {
  // 如果是可预知的业务错误
  if (err.isOperational) {
    res.status(err.statusCode).json({
      status: err.status,
      message: err.message,
    })

    // 不可预知的错误
  } else {
    console.log('ERROR ❌', err)

    res.status(500).json({
      status: 'error',
      message: '发生错误！',
    })
  }
}

// 处理JsonWebTokenError
const handleJwtError = () => new AppError('无效的token，请重新登录', 401)

// 处理TokenExpiredError
const handleTokenExpiredError = () => new AppError('你的 token 已经过期，请重新登录', 401)

// 处理键值重复的错误
const handleDuplicateFieldsDB = (error) => {
  const key = Object.keys(error.keyValue)[0]
  const value = error.keyValue[key]
  const message = `重复的键值 ${value} ，请使用别的值`

  return new AppError(message, 400)
}

// 处理ValidationError error
const handleValidationError = (error) => {
  const errors = Object.values(error.errors).map((el) => el.message)

  const message = `输入的信息无效：${errors.join(', ')}`
  return new AppError(message, 400)
}

/**
 * 错误处理中间件
 * 为了提升用户体验，开发环境和生产环境的错误提示是不一样的
 * @param {*} err
 * @param {*} req
 * @param {*} res
 * @param {*} next
 */
module.exports = (err, req, res, next) => {
  err.statusCode = err.statusCode || 500
  err.status = err.status || 'error'

  if (process.env.NODE_ENV === 'development') {
    sendErrorDev(err, res)
  } else if (process.env.NODE_ENV === 'production') {
    let error = { ...err }
    error.message = err.message
    error.name = err.name
    error.errors = err.errors

    // 判断错误类型
    if (error.name === 'JsonWebTokenError') {
      error = handleJwtError(error)
    } else if (error.name === 'TokenExpiredError') {
      error = handleTokenExpiredError(error)
    } else if (error.code === 11000) {
      error = handleDuplicateFieldsDB(error)
    } else if (error.name === 'ValidationError') {
      error = handleValidationError(error)
    }

    sendErrorProd(error, res)
  }
}
